﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using RayDen.Library.Components.Surface;
using RayDen.MaterialManager;
using RayDen.MaterialManager.Controllers;

namespace RayDen.MaterialManager
{
    public partial class EditorForm : Form
    {
        public ApplicationController Controller;
        public RenderingController RenderController;
        public SurfaceMaterialEditController EditController;
        protected bool rendering;

        public EditorForm()
        {
            Controller = new ApplicationController();
            EditController = new SurfaceMaterialEditController();
            InitializeComponent();
        }

        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            BindList();
        }

        protected override void OnClosed(EventArgs e)
        {
            var def = ConfigurationManager.AppSettings.Get("DefaultVault");
            this.Controller.CurrentVault.SaveXml(def ?? "DefaultMaterialVault.xml", this.Controller.KnownTypes);
            if (this.RenderController != null)
            {
                this.RenderController.Dispose();
            }
            base.OnClosed(e);
        }
        
        private void BindList()
        {
            lstMaterials.DataSource = Controller.Materials;
        }

        private void tabPage1_Click(object sender, EventArgs e)
        {

        }

        private void loadFrommtlToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                Exec(() => Controller.OpenMtl(openFileDialog1.FileName), "Open " + openFileDialog1.FileName + " file");
            }
        }

        private void Exec(Action command, string message)
        {
            command();
            BindList();
            this.toolStrip1.Text = message;
        }

        private void WarnExec(Action command, string warn, string message, bool bindList = false)
        {
            if (MessageBox.Show(warn, "Are you sure", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) ==
                DialogResult.OK)
            {
                command();
                if (bindList)
                    BindList();
                this.toolStrip1.Text = message;
            }
        }

        private void openVaultToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog2.ShowDialog() == DialogResult.OK)
            {
                Exec(() => Controller.OpenVault(openFileDialog2.FileName), "Open " + openFileDialog2.FileName + " Vault");
            }
        }

        private void lstMaterials_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Delete && lstMaterials.SelectedIndex >= 0)
            {
                
            }
        }

        private void saveVaultToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                Exec(() => Controller.SaveVault(saveFileDialog1.FileName), "Saved " + saveFileDialog1.FileName + " Vault");
            }
        }

        private void lstMaterials_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lstMaterials.SelectedIndex >= 0)
            {                
                Select(lstMaterials.SelectedIndex);
            }
        }

        private void Select(int index)
        {
            var mat = Controller.CurrentVault.ToArray()[index];
            lstBrdfs.DataSource = mat.ToList();
            lstBrdfs.DisplayMember = "Name";
            EditController.Material = mat;

            comboBox1.DisplayMember = "Name";
            comboBox1.DataSource = mat.ToList();
            comboBox1.SelectedIndex = 0;
            propertyGrid1.SelectedObject = comboBox1.SelectedItem;
            if (this.RenderController != null)
            {
                this.RenderController.Dispose();
            }

            RenderController = new RenderingController(mat, ctlPreviewCanvas.ClientRectangle.Width, ctlPreviewCanvas.ClientRectangle.Height);
            Msg( (mat).Name + " selected");
        }


        private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
        {
            propertyGrid1.SelectedObject = comboBox1.SelectedItem;
        }

        private void btnSelectData_Click(object sender, EventArgs e)
        {
            WarnExec(() =>
                {
                    propertyGrid1.SelectedObject = ((SurfaceBsdf)comboBox1.SelectedItem).MData;        
                }, "Select Material Data?","Material Data of " +((SurfaceBsdf)comboBox1.SelectedItem).Name + " selected");
            
        }

        private async void toolStripButton1_Click(object sender, EventArgs e)
        {
            if (RenderController!=null)
            {

                this.btnStartRender.Enabled = false;
                var renderTask = Task.Factory.StartNew(this.StartRenderFrame);
                await renderTask;
                this.btnStartRender.Enabled = true;
                Msg( @"Rendering started");
            }
        }

        private void tabControl1_TabIndexChanged(object sender, EventArgs e)
        {
            if (rendering)
            {
                this.InterrupRendering();
            }
        }

        private void StartRenderFrame()
        {
            try
            {
                rendering = true;
                this.RenderController.StartRender(this.ctlPreviewCanvas);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "Exception", MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
            }
        }

        private void InterrupRendering()
        {
            rendering = false;
            this.RenderController.Interrupt();
        }

        private void ctlPreviewCanvas_Paint(object sender, PaintEventArgs e)
        {
            if (this.rendering)
            {
               
            }
        }

        private void btnStopRender_Click(object sender, EventArgs e)
        {
            InterrupRendering();
            Msg( "Rendering interrupted");
            Refresh();
        }

        private void btnAddBrdf_Click(object sender, EventArgs e)
        {
            

        }

        private void btnSelectRoot_Click(object sender, EventArgs e)
        {
            var mat = Controller.CurrentVault.ToArray()[lstMaterials.SelectedIndex];
            propertyGrid1.SelectedObject = mat;
            Msg( "Root Material Selected");
        }

        [SuppressUnmanagedCodeSecurity]
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

        protected void Msg(string message)
        {
            this.lblMessage.Text = message;
            this.Text =  message;
            this.Refresh();
        }

        private bool AppStillIdle()
        {
            Message msg;
            return !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
        }

        public void OnIdle(object sender, EventArgs e)
        {
            while (AppStillIdle())
            {
                if (rendering && tabControl1.SelectedIndex == 1)
                {
                    var film = RenderController.RenderConfig.GetImagePlane();
                    if (film != null)
                    {
                        film.UpdateScreenBuffer();
                        var pixels = film.GetScreenBuffer();
                        if (ctlPreviewCanvas.Image == null)
                        {
                            ctlPreviewCanvas.Image = new Bitmap(RenderController.RenderConfig.Width, RenderController.RenderConfig.Height);
                        }
                        var bmp = (Bitmap)ctlPreviewCanvas.Image;
                        var offset = 0;
                        for (int ii = 0; ii < RenderController.RenderConfig.Height; ii++)
                        {
                            for (int j = 0; j < RenderController.RenderConfig.Width; j++, offset += 3)
                            {
                                var pix = Color.FromArgb((int)Math.Abs(255.0 * pixels[offset]), (int)Math.Abs(255.0 * pixels[offset + 1]), (int)Math.Abs(255.0 * pixels[offset + 2]));
                                bmp.SetPixel(j, (bmp.Height-1) - ii, pix);
                            }
                        }
                        lblStats.Text = RenderController.RenderConfig.GetStats();
                        ctlPreviewCanvas.Invalidate();
                    }
                }

            }
        }

        private void toolStripButton1_Click_1(object sender, EventArgs e)
        {
            this.Controller.AddMaterial();
            this.Refresh();
        }

        private void btnDeleteBrdf_Click(object sender, EventArgs e)
        {

        }

        private void lstBrdfs_SelectedIndexChanged(object sender, EventArgs e)
        {
            var list = (List<SurfaceBsdf>) lstBrdfs.DataSource;
            var bsdf = list[lstBrdfs.SelectedIndex];
            propertyGrid1.SelectedObject = bsdf;

            Msg("Elementary brdf "+bsdf.BrdfName + " selected");
        }

        private void lstMaterials_DrawItem(object sender, DrawItemEventArgs e)
        {
            var item = lstMaterials.Items[e.Index] as string; // Get the current item and cast it to MyListBoxItem
            Color itemColor = Color.Wheat;
            

            if (item != null)
            {
                if (item.Contains("Metal"))
                {
                    itemColor = Color.Blue;
                }else
                    if (item.Contains("ArchGlass"))
                    {
                        itemColor = Color.Turquoise;
                    }else
                if (item.Contains("Glass"))
                {
                    itemColor = Color.Aqua;
                }
                else
                if (item.Contains("MatteMirror"))
                {
                    itemColor = Color.Violet;
                }else 
                    if (item.Contains("Mirror"))
                    {
                        itemColor = Color.Silver;
                    }
                    else if (item.Contains("Alloy"))
                    {
                        itemColor = Color.SpringGreen;
                    }




                e.Graphics.DrawString( // Draw the appropriate text in the ListBox
                    item, // The message linked to the item
                    lstMaterials.Font, // Take the font from the listbox
                    new SolidBrush(itemColor), // Set the color 
                    0, // X pixel coordinate
                    e.Index * lstMaterials.ItemHeight // Y pixel coordinate.  Multiply the index by the ItemHeight defined in the listbox.
                );
            }
            else
            {
                // The item isn't a MyListBoxItem, do something about it
            }
        }

        private void openFileDialog1_FileOk(object sender, CancelEventArgs e)
        {

        }
    }
}
